<!DOCTYPE html>
<html lang="en-us" dir="ltr" itemscope itemtype="http://schema.org/Article" data-r-output-format="html">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0, minimum-scale=1.0">
    <meta name="generator" content="Hugo 0.147.3">
    <meta name="generator" content="Relearn 8.2.0+220806ce3ced8264fbdd693f77f28216a8a1b6ae">
    <meta name="description" content="If you are not yet familiar with Go modules in general (outside of gokrazy), please read the Go wiki page on Modules first.
Since September 2022, the gokrazy packer builds each package in its own build directory, with its own go.mod and go.sum files. This is done for isolation: if you update one program, that will not have any effect on the other programs you include in your gokrazy instance.
Example setup Throughout this page, let’s assume your gokrazy instance is named scanner, and will hence store its files in ~/gokrazy/scanner. A single additional program, scan2drive, is included:">
    <meta name="author" content="">
    <meta name="twitter:card" content="summary">
    <meta name="twitter:title" content="Working with Go modules :: gokrazy">
    <meta name="twitter:description" content="If you are not yet familiar with Go modules in general (outside of gokrazy), please read the Go wiki page on Modules first.
Since September 2022, the gokrazy packer builds each package in its own build directory, with its own go.mod and go.sum files. This is done for isolation: if you update one program, that will not have any effect on the other programs you include in your gokrazy instance.
Example setup Throughout this page, let’s assume your gokrazy instance is named scanner, and will hence store its files in ~/gokrazy/scanner. A single additional program, scan2drive, is included:">
    <meta property="og:url" content="https://gokrazy.org/development/modules/index.html">
    <meta property="og:site_name" content="gokrazy">
    <meta property="og:title" content="Working with Go modules :: gokrazy">
    <meta property="og:description" content="If you are not yet familiar with Go modules in general (outside of gokrazy), please read the Go wiki page on Modules first.
Since September 2022, the gokrazy packer builds each package in its own build directory, with its own go.mod and go.sum files. This is done for isolation: if you update one program, that will not have any effect on the other programs you include in your gokrazy instance.
Example setup Throughout this page, let’s assume your gokrazy instance is named scanner, and will hence store its files in ~/gokrazy/scanner. A single additional program, scan2drive, is included:">
    <meta property="og:locale" content="en_us">
    <meta property="og:type" content="article">
    <meta property="article:section" content="Package development">
    <meta itemprop="name" content="Working with Go modules :: gokrazy">
    <meta itemprop="description" content="If you are not yet familiar with Go modules in general (outside of gokrazy), please read the Go wiki page on Modules first.
Since September 2022, the gokrazy packer builds each package in its own build directory, with its own go.mod and go.sum files. This is done for isolation: if you update one program, that will not have any effect on the other programs you include in your gokrazy instance.
Example setup Throughout this page, let’s assume your gokrazy instance is named scanner, and will hence store its files in ~/gokrazy/scanner. A single additional program, scan2drive, is included:">
    <meta itemprop="wordCount" content="498">
    <title>Working with Go modules :: gokrazy</title>
    <link href="/images/favicon.png?1763621646" rel="icon" type="image/png">
    <link href="/css/auto-complete/auto-complete.min.css?1763621646" rel="stylesheet">
    <script src="/js/auto-complete/auto-complete.min.js?1763621646" defer></script>
    <script src="/js/search-lunr.min.js?1763621646" defer></script>
    <script src="/js/search.min.js?1763621646" defer></script>
    <script>
      window.relearn = window.relearn || {};
      window.relearn.index_js_url="/searchindex.en.js?1763621646";
    </script>
    <script src="/js/lunr/lunr.min.js?1763621646" defer></script>
    <script src="/js/lunr/lunr.stemmer.support.min.js?1763621646" defer></script>
    <script src="/js/lunr/lunr.multi.min.js?1763621646" defer></script>
    <script src="/js/lunr/lunr.en.min.js?1763621646" defer></script>
    <script>
      window.relearn = window.relearn || {};
      window.relearn.contentLangs=['en'];
    </script>
    <link href="/fonts/fontawesome/css/fontawesome-all.min.css?1763621646" rel="stylesheet" media="print" onload="this.media='all';this.onload=null;"><noscript><link href="/fonts/fontawesome/css/fontawesome-all.min.css?1763621646" rel="stylesheet"></noscript>
    <link href="/css/perfect-scrollbar/perfect-scrollbar.min.css?1763621646" rel="stylesheet">
    <link href="/css/theme.min.css?1763621646" rel="stylesheet">
    <link href="/css/format-html.min.css?1763621646" rel="stylesheet" id="R-format-style">
    <script>
      window.relearn = window.relearn || {};
      // configuration
      window.relearn.min = `.min`;
      window.relearn.path='\/development\/modules\/index.html';
      window.relearn.relBasePath='..\/..';
      window.relearn.relBaseUri='..\/..';
      window.relearn.absBaseUri='https:\/\/gokrazy.org';
      window.relearn.disableInlineCopyToClipboard=true;
      window.relearn.enableBlockCodeWrap=true;
      // legal
      window.relearn.getItem = (s,n) => {return s.getItem(n)};
      window.relearn.setItem = (s,n,v) => {return s.setItem(n,v)};
      window.relearn.removeItem = (s,n) => {return s.removeItem(n)};
      // translations
      window.T_Copy_to_clipboard = `Copy text to clipboard`;
      window.T_Copied_to_clipboard = `Text copied to clipboard!`;
      window.T_Link_copied_to_clipboard = `Link copied to clipboard!`;
      window.T_Reset_view = `Reset view`;
      window.T_View_reset = `View reset!`;
      window.T_No_results_found = `No results found for "{0}"`;
      window.T_N_results_found = `{1} results found for "{0}"`;
      window.T_Browser_unsupported_feature = `This browser doesn't support this feature`;
      // variant stuff
      window.relearn.themevariants = [ 'auto' ];
      window.relearn.customvariantname = "my-custom-variant";
      window.relearn.changeVariant = function(variant) {
        var oldVariant = document.documentElement.dataset.rThemeVariant;
        window.relearn.setItem(window.localStorage, window.relearn.absBaseUri + "/variant", variant);
        document.documentElement.dataset.rThemeVariant = variant;
        if (oldVariant != variant) {
          document.dispatchEvent( new CustomEvent('themeVariantLoaded', { detail: { variant, oldVariant } }) );
          window.relearn.markVariant();
        }
      }
      window.relearn.markVariant = function() {
        var variant = window.relearn.getItem(window.localStorage, window.relearn.absBaseUri + "/variant");
        document.querySelectorAll(".R-variantswitcher select").forEach((select) => {select.value = variant;});
      }
      window.relearn.initVariant = function() {
        var variant = window.relearn.getItem(window.localStorage, window.relearn.absBaseUri + "/variant") ?? "";
        if( variant == window.relearn.customvariantname ){
        }else if( !variant || !window.relearn.themevariants.includes(variant) ){
          variant = window.relearn.themevariants[0];
          window.relearn.setItem(window.localStorage, window.relearn.absBaseUri + "/variant", variant);
        }
        document.documentElement.dataset.rThemeVariant = variant;
      }
      window.relearn.initVariant();
      window.relearn.markVariant();
    </script>
  </head>
  <body class="mobile-support html" data-url="/development/modules/index.html">
    <div id="R-body" class="default-animation">
      <div id="R-body-overlay"></div>
      <nav id="R-topbar">
        <div class="topbar-wrapper">
          <div class="topbar-sidebar-divider"></div>
          <div class="topbar-area topbar-area-start" data-area="start">
            <div class="topbar-button topbar-button-sidebar" data-content-empty="disable" data-width-s="show" data-width-m="hide" data-width-l="hide">
<span class="btn cstyle button link noborder notitle interactive"><button onclick="toggleNav()" type="button" title="Menu (CTRL&#43;ALT&#43;n)"><i class="fa-fw fas fa-bars"></i></button></span>
            </div>
            <div class="topbar-button topbar-button-toc" data-content-empty="hide" data-width-s="show" data-width-m="show" data-width-l="show">
<span class="btn cstyle button link noborder notitle interactive"><button onclick="toggleTopbarFlyout(this)" type="button" title="Table of Contents (CTRL&#43;ALT&#43;t)"><i class="fa-fw fas fa-list-alt"></i></button></span>
              <div class="topbar-content">
                <div class="topbar-content-wrapper">
<nav class="TableOfContents">
  <ul>
    <li><a href="#example-setup">Example setup</a></li>
    <li><a href="#top-level-gomod-template">Top-level go.mod template</a></li>
    <li><a href="#building-local-code-the-replace-directive">Building local code: the replace directive</a></li>
    <li><a href="#influencing-the-granularity">Influencing the granularity</a></li>
  </ul>
</nav>
                </div>
              </div>
            </div>
          </div>
          <ol class="topbar-breadcrumbs breadcrumbs highlightable" itemscope itemtype="http://schema.org/BreadcrumbList"><li itemscope itemtype="https://schema.org/ListItem" itemprop="itemListElement" class=""><a itemprop="item" href="/index.html"><span itemprop="name">gokrazy Go appliances</span></a><meta itemprop="position" content="1">&nbsp;>&nbsp;</li><li itemscope itemtype="https://schema.org/ListItem" itemprop="itemListElement" class=""><a itemprop="item" href="/development/index.html"><span itemprop="name">Package development</span></a><meta itemprop="position" content="2">&nbsp;>&nbsp;</li><li itemscope itemtype="https://schema.org/ListItem" itemprop="itemListElement" class=""><span itemprop="name">Working with Go modules</span><meta itemprop="position" content="3"></li>
          </ol>
          <div class="topbar-area topbar-area-end" data-area="end">
            <div class="topbar-button topbar-button-edit" data-content-empty="disable" data-width-s="area-more" data-width-m="show" data-width-l="show">
<span class="btn cstyle button link noborder notitle interactive"><a href="https://github.com/gokrazy/gokrazy/tree/master/website/content/development/modules.markdown" rel="external" title="Edit (CTRL+ALT+w)"><i class="fa-fw fas fa-pen"></i></a></span>
            </div>
            <div class="topbar-button topbar-button-more" data-content-empty="hide" data-width-s="show" data-width-m="show" data-width-l="show">
<span class="btn cstyle button link noborder notitle interactive"><button onclick="toggleTopbarFlyout(this)" type="button" title="More"><i class="fa-fw fas fa-ellipsis-v"></i></button></span>
              <div class="topbar-content">
                <div class="topbar-content-wrapper">
                  <div class="topbar-area topbar-area-more" data-area="more">
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </nav>
      <div id="R-main-overlay"></div>
      <main id="R-body-inner" class="highlightable development" tabindex="-1">
        <div class="flex-block-wrapper">
<article class="default">
  <header class="headline">
  </header>

<h1 id="working-with-go-modules">Working with Go modules</h1>

<p>If you are not yet familiar with Go modules in general (outside of gokrazy),
please <a href="https://github.com/golang/go/wiki/Modules" rel="external">read the Go wiki page on
Modules</a> first.</p>
<p><a href="https://github.com/gokrazy/tools/commit/c3979e1ceb3ea4fe02b78b2c1493c8128a5dc3f9" rel="external">Since September
2022</a>,
the gokrazy packer builds each package in its own build directory, with its own
<code>go.mod</code> and <code>go.sum</code> files. This is done for isolation: if you update one
program, that will not have any effect on the other programs you include in your
gokrazy instance.</p>
<h2 id="example-setup">Example setup<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>Throughout this page, let’s assume your gokrazy instance is named <code>scanner</code>, and
will hence store its files in <code>~/gokrazy/scanner</code>. A single additional program,
<code>scan2drive</code>, is included:</p>
<div class="highlight actionbar-wrapper wrap-code" dir="auto"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gok -i scanner new
</span></span><span style="display:flex;"><span>gok -i scanner add github.com/stapelberg/scan2drive/cmd/scan2drive</span></span></code></pre></div>
<p>When building this instance (using <code>gok overwrite</code> initially, or <code>gok update</code>
afterwards), the <code>gok</code> CLI will create the following build directory structure:</p>
<div class="highlight actionbar-wrapper wrap-code" dir="auto"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>% cd ~/gokrazy/scanner
</span></span><span style="display:flex;"><span>% find . -name go.mod
</span></span><span style="display:flex;"><span>./builddir/init/go.mod
</span></span><span style="display:flex;"><span>./builddir/github.com/stapelberg/scan2drive/cmd/scan2drive/go.mod
</span></span><span style="display:flex;"><span>./builddir/github.com/gokrazy/serial-busybox/go.mod
</span></span><span style="display:flex;"><span>./builddir/github.com/gokrazy/hello/go.mod
</span></span><span style="display:flex;"><span>./builddir/github.com/gokrazy/fbstatus/go.mod
</span></span><span style="display:flex;"><span>./builddir/github.com/gokrazy/gokrazy/go.mod
</span></span><span style="display:flex;"><span>./builddir/github.com/gokrazy/gokrazy/cmd/randomd/go.mod
</span></span><span style="display:flex;"><span>./builddir/github.com/gokrazy/gokrazy/cmd/ntp/go.mod
</span></span><span style="display:flex;"><span>./builddir/github.com/gokrazy/gokrazy/cmd/dhcp/go.mod
</span></span><span style="display:flex;"><span>./builddir/github.com/gokrazy/rpi-eeprom/go.mod
</span></span><span style="display:flex;"><span>./builddir/github.com/gokrazy/firmware/go.mod
</span></span><span style="display:flex;"><span>./builddir/github.com/gokrazy/kernel/go.mod</span></span></code></pre></div>
<p>You can see that there is one subdirectory for each package listed in the
<code>Packages</code> field of your instance’s <code>config.json</code> (see <code>gok edit</code>), which
includes the explicitly added <code>scan2drive</code>, plus a couple extra ones that
gokrazy always installs, e.g. <code>github.com/gokrazy/gokrazy/cmd/dhcp</code>.</p>
<h2 id="top-level-gomod-template">Top-level go.mod template<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>If you want to influence the content of any <em>newly created</em> <code>go.mod</code> (no effect
on existing <code>go.mod</code> files), you can create a <code>go.mod</code> template in your instance
directory: <code>~/gokrazy/scanner/go.mod</code>.</p>
<h2 id="building-local-code-the-replace-directive">Building local code: the replace directive<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>Go modules are loaded from the internet by default and are stored read-only on
disk.</p>
<p>If you want to make the gokrazy packer pick up a local working copy with
not-yet-published code, or a working copy with local changes to existing code,
use <code>gok add</code> with a directory name:</p>
<div class="highlight actionbar-wrapper wrap-code" dir="auto"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Create a local working copy in whichever directory you like.</span>
</span></span><span style="display:flex;"><span>% cd ~/projects
</span></span><span style="display:flex;"><span>% git clone https://github.com/stapelberg/scan2drive
</span></span><span style="display:flex;"><span>% cd scan2drive
</span></span><span style="display:flex;"><span><span style="color:#75715e"># make some changes</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>% gok -i scan2drive add ./cmd/scan2drive</span></span></code></pre></div>
<p>The <code>gok</code> CLI will set up the <code>replace</code> directive for you. For more details on
<code>replace</code>, see <a href="https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive" rel="external">the Go
wiki</a>.</p>
<h2 id="influencing-the-granularity">Influencing the granularity<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>Often, one Go <em>package</em> will be the only package you use from a certain Go
<em>module</em>. But this isn’t always the case: for example, the system packages
<code>github.com/gokrazy/gokrazy/cmd/dhcp</code> and <code>github.com/gokrazy/gokrazy/cmd/ntp</code>
both come from the <code>github.com/gokrazy/gokrazy</code> module.</p>
<p>The packer will by default create a separate builddir, including a separate
<code>go.mod</code> and <code>go.sum</code>, for each package, even when they come from the same
module.</p>
<p>If you want to add module-wide replace directives to your <code>go.mod</code> file, you can
influence the granularity at which gokr-packer works as follows.</p>
<p>Move the <code>go.mod</code>/<code>go.sum</code> files to the directory level within the <code>builddir/</code>
hierarchy at which you would like to work. gokr-packer will look for
<code>go.mod</code>/<code>go.sum</code> files at the package level, going one level up until it finds
the files.</p>
<p>Hence, you can use the following locations, ordered from finest to coarsest
granularity:</p>
<ol>
<li>
<p>per-package builddir (default), e.g.:
<code>builddir/github.com/gokrazy/gokrazy/cmd/dhcp/go.mod</code></p>
</li>
<li>
<p>per-module builddir (convenient when working with replace directives), e.g.:
<code>builddir/github.com/gokrazy/gokrazy/go.mod</code></p>
</li>
<li>
<p>per-org builddir (convenient for wide-reaching replace directives), e.g.:
<code>builddir/github.com/gokrazy/go.mod</code></p>
</li>
<li>
<p>single builddir, preserving the previous behavior, e.g.:
<code>builddir/go.mod</code></p>
</li>
</ol>

  <footer class="footline">
  </footer>
</article>
        </div>
      </main>
    </div>
    <aside id="R-sidebar" class="default-animation">
      <div id="R-header-topbar" class="default-animation"></div>
      <div id="R-header-wrapper" class="default-animation">
        <div id="R-header" class="default-animation">
<img src="/logo.svg" width="100%" alt="gokrazy logo" title="gokrazy logo">

        </div>
        <search><form action="/search/index.html" method="get">
          <div class="searchbox default-animation">
            <button class="search-detail" type="submit" title="Search (CTRL+ALT+f)"><i class="fas fa-search"></i></button>
            <label class="a11y-only" for="R-search-by">Search</label>
            <input data-search-input id="R-search-by" name="search-by" class="search-by" type="search" placeholder="Search...">
            <button class="search-clear" type="button" data-search-clear="" title="Clear search"><i class="fas fa-times" title="Clear search"></i></button>
          </div>
        </form></search>
      </div>
      <div id="R-homelinks" class="default-animation homelinks">
        <div class="R-menu-divider default-animation">
          <hr class="padding">
        </div>
        <div class="R-sidebarmenu R-shortcutmenu-homelinks">
          <ul class="space collapsible-menu">
            <li class="" data-nav-url="/index.html"><a class="padding" href="/index.html"><i class="fa-fw fas fa-home"></i> Home</a></li>
          </ul>
        </div>
        <div class="R-menu-divider default-animation">
          <hr class="padding">
        </div>
        <div class="R-sidebarmenu R-shortcutmenu-headercontrols">
          <ul class="">
          </ul>
        </div>
        <div class="R-menu-divider default-animation">
          <hr class="padding">
        </div>
      </div>
      <div id="R-content-wrapper" class="highlightable">
        <div class="R-sidebarmenu R-shortcutmenu-main">
          <ul class="enlarge morespace collapsible-menu">
            <li class="" data-nav-url="/quickstart/index.html"><a class="padding" href="/quickstart/index.html"><b>1. </b>Quickstart</a></li>
            <li class="" data-nav-url="/platforms/index.html"><a class="padding" href="/platforms/index.html"><b>2. </b>Supported platforms</a></li>
            <li class="" data-nav-url="/userguide/index.html"><a class="padding" href="/userguide/index.html"><b>3. </b>Configuration</a><ul id="R-subsections-74b4df3a3d9365853f09e9f1dfe7bbba" class="collapsible-menu"></ul></li>
            <li class="" data-nav-url="/packages/index.html"><a class="padding" href="/packages/index.html"><b>4. </b>Available packages</a><ul id="R-subsections-bdf1e8ce92aa8ff6883c617f10480430" class="collapsible-menu"></ul></li>
            <li class="parent " data-nav-url="/development/index.html"><a class="padding" href="/development/index.html"><b>5. </b>Package development</a><ul id="R-subsections-54b0ce3c97b111c8cd4d107325fd580c" class="collapsible-menu">
            <li class="" data-nav-url="/development/process-interface/index.html"><a class="padding" href="/development/process-interface/index.html">Process interface / requirements</a></li>
            <li class="" data-nav-url="/development/gpio/index.html"><a class="padding" href="/development/gpio/index.html">Controlling a GPIO input/output pin</a></li>
            <li class="active " data-nav-url="/development/modules/index.html"><a class="padding" href="/development/modules/index.html">Working with Go modules</a></li>
            <li class="" data-nav-url="/development/buildtags/index.html"><a class="padding" href="/development/buildtags/index.html">The gokrazy Go build tag</a></li>
            <li class="" data-nav-url="/development/non-go/index.html"><a class="padding" href="/development/non-go/index.html">Non-Go Prototyping</a></li></ul></li>
          </ul>
        </div>
        <div class="R-sidebarmenu R-shortcutmenu-shortcuts">
          <div class="nav-title padding">More</div>
          <ul class="space collapsible-menu">
            <li class="" data-nav-url="https://github.com/gokrazy/gokrazy"><a class="padding" href="https://github.com/gokrazy/gokrazy" rel="external"><i class='fab fa-github'></i> GitHub repo</a></li>
          </ul>
        </div>
        <div id="R-footer-margin"></div>
        <div class="R-menu-divider default-animation">
          <hr class="padding">
        </div>
        <div class="R-sidebarmenu R-shortcutmenu-footercontrols">
          <ul class="">
          </ul>
        </div>
<div id="R-footer"><p>Built with <a href="https://github.com/McShelby/hugo-theme-relearn" title="love"><i class="fas fa-heart"></i></a> by <a href="https://gohugo.io/">Hugo</a></p></div>
      </div>
    </aside>
    <script src="/js/perfect-scrollbar/perfect-scrollbar.min.js?1763621646" defer></script>
    <script src="/js/theme.min.js?1763621646" defer></script>
    <div id="toast-container" role="status" aria-live="polite" aria-atomic="false"></div>
  </body>
</html>
